package cn.thornbird.orgsync.controller;

import cn.thornbird.orgsync.config.CsrfFilter;
import cn.thornbird.orgsync.util.CookieUtil;
import lombok.AllArgsConstructor;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.web.WebAttributes;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

@Controller
@AllArgsConstructor
public class PageController {

    public static final String INVALID_CREDENTIALS = "无效的凭据";

    private final HttpServletRequest request;

    @GetMapping("/")
    public String home() {
        return "redirect:/index";
    }

    @GetMapping("/index")
    public String index() {
        return "index/index";
    }

    @GetMapping("/sign_in")
    public String signIn(Model model) {
        boolean loginError = matches(request, "/sign_in?error");
        String errorMessage = loginError ? getLoginErrorMessage(request) : "";
        model.addAttribute("loginError", loginError);
        model.addAttribute("errorMessage", errorMessage);
        setCsrfToken(model);
        return "sign_in";
    }

    @GetMapping("/sign_up")
    public String signUp(Model model) {
        setCsrfToken(model);
        return "sign_up";
    }

    @GetMapping("/reset_password")
    public String resetPassword(Model model) {
        setCsrfToken(model);
        return "reset_password";
    }

    private String getLoginErrorMessage(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null) {
            Object exception = session.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
            if (exception instanceof AuthenticationException) {
                AuthenticationException authenticationException = (AuthenticationException) exception;
                return getErrorMessage(authenticationException);
            }
        }
        return INVALID_CREDENTIALS;
    }

    private String getErrorMessage(AuthenticationException exception) {
        String message;
        MessageSourceAccessor accessor = SpringSecurityMessageSource.getAccessor();
        if (exception instanceof BadCredentialsException) {
            message = accessor.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials");
        } else if (exception instanceof DisabledException) {
            message = accessor.getMessage("AccountStatusUserDetailsChecker.disabled");
        } else if (exception instanceof AccountExpiredException) {
            message = accessor.getMessage("AccountStatusUserDetailsChecker.expired");
        } else if (exception instanceof CredentialsExpiredException) {
            message = accessor.getMessage("AccountStatusUserDetailsChecker.credentialsExpired");
        } else if (exception instanceof LockedException) {
            message = accessor.getMessage("AccountStatusUserDetailsChecker.locked");
        } else {
            message = exception.getMessage();
        }
        return message;
    }

    private boolean matches(HttpServletRequest request, String url) {
        if (!"GET".equals(request.getMethod()) || url == null) {
            return false;
        }
        String uri = request.getRequestURI();
        int pathParamIndex = uri.indexOf(';');
        if (pathParamIndex > 0) {
            uri = uri.substring(0, pathParamIndex);
        }
        if (request.getQueryString() != null) {
            uri += "?" + request.getQueryString();
        }
        if ("".equals(request.getContextPath())) {
            return uri.equals(url);
        }
        return uri.equals(request.getContextPath() + url);
    }

    private void setCsrfToken(Model model) {
        String token = CookieUtil.getCookieValue(request, CsrfFilter.CSRF_TOKEN_NAME);
        model.addAttribute("csrfTokenName", CsrfFilter.REQUEST_CSRF_TOKEN_NAME);
        model.addAttribute("csrfTokenValue", token);
    }

}
